package com.ycy.smartcourseselection.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ycy.smartcourseselection.common.PageParamsDTO;
import com.ycy.smartcourseselection.common.PageResVO;
import com.ycy.smartcourseselection.common.ResCodeEnum;
import com.ycy.smartcourseselection.common.ResData;
import com.ycy.smartcourseselection.dto.courseSelection.*;
import com.ycy.smartcourseselection.dto.student.FindAllStuDTO;
import com.ycy.smartcourseselection.entity.CoSelection;
import com.ycy.smartcourseselection.entity.Course;
import com.ycy.smartcourseselection.entity.Student;
import com.ycy.smartcourseselection.entity.Teacher;
import com.ycy.smartcourseselection.exception.CustomException;
import com.ycy.smartcourseselection.mapper.ICoSelectionMapper;
import com.ycy.smartcourseselection.utils.PageUtils;
import com.ycy.smartcourseselection.utils.WrapperUtils;
import com.ycy.smartcourseselection.vo.courseSelection.CoSelectionVO;
import com.ycy.smartcourseselection.vo.course.CourseVO;
import com.ycy.smartcourseselection.vo.courseSelection.FileCoSelectionVO;
import com.ycy.smartcourseselection.vo.student.FileStuVO;
import com.ycy.smartcourseselection.vo.student.StudentVO;
import com.ycy.smartcourseselection.vo.teacher.TeacherVO;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

@Service
public class CoSelectionService extends ServiceImpl<ICoSelectionMapper, CoSelection> {
    PageUtils<CoSelection, CoSelectionVO> coSelectionPageUtils = new PageUtils<>();
    @Resource
    StudentService studentService;
    @Resource
    TeacherService teacherService;
    @Resource
    CourseService courseService;

    //添加选课记录(没有修改)
    public ResData<Boolean> saveCoSelection(CoSelectionDTO coSelectionDTO) {
        String studentId = coSelectionDTO.getStudentId();
        String courseId = coSelectionDTO.getCourseId();

        if(StrUtil.isBlank(studentId)) throw new CustomException(ResCodeEnum.BAD_REQUEST,"学号不能为空");
        if(StrUtil.isBlank(courseId)) throw new CustomException(ResCodeEnum.BAD_REQUEST,"课程id不能为空");

        //判断课程是否满员
        QueryWrapper<Course> checkMaxWrapper = new QueryWrapper<>();
        checkMaxWrapper.eq("id",courseId);
        checkMaxWrapper.select("max_person","selected_num");
        List<Course> courseList = courseService.list(checkMaxWrapper);
        if(courseList.size() == 0) throw new CustomException(ResCodeEnum.BAD_REQUEST,"该课程不存在");
        if(courseList.size() > 1) throw new CustomException(ResCodeEnum.BAD_REQUEST,"错误地存在多个该课程信息");
        Course course = courseList.get(0);
        if(course.getMaxPerson() == course.getSelectedNum()) throw new CustomException(ResCodeEnum.BAD_REQUEST,"该课程已满员");

        //判断该学生是否已经选过该课程
        QueryWrapper<CoSelection> wrapper = new QueryWrapper<>();
        wrapper.eq("student_id",studentId);
        wrapper.eq("course_id",courseId);
        List<CoSelection> sameList = list(wrapper);
        if(sameList.size() != 0) throw new CustomException(ResCodeEnum.BAD_REQUEST,"该学号的学生已选择该课程");

        //保存在选课记录表中
        CoSelection coSelection = new CoSelection();
        BeanUtil.copyProperties(coSelectionDTO,coSelection);
        boolean isSave = saveOrUpdate(coSelection);
        if(!isSave) throw new CustomException(ResCodeEnum.SERVER_ERROR,"保存选课记录错误");

        boolean isUpdate = courseService.saveOrUpdate(getSelectedNumEntity(courseId));
        if(!isUpdate) throw new CustomException(ResCodeEnum.SERVER_ERROR,"选课更新选课人数错误");

        return ResData.success(true);
    }
    //批量取消选课(彻底删除)
    public ResData<Boolean> trueDeleteCoSelectionByIds(CoSeDeleteDTO coSeDeleteDTO) {
        ArrayList<String> coSeIdList = coSeDeleteDTO.getCoSeIdList();
        ArrayList<String> courseIdList = coSeDeleteDTO.getCourseIdList();

        //从选课记录表中删除记录
        boolean isDelete = removeBatchByIds(coSeIdList);
        if(!isDelete) throw new CustomException(ResCodeEnum.SERVER_ERROR,"删除选课记录错误");

        //getSelectedNumEntity方法查询出最新的选课人数，并把course对象装进数组中。
        ArrayList<Course> courseList = new ArrayList<>();
        for (String courseId : courseIdList) courseList.add(getSelectedNumEntity(courseId));

        //批量更新课程信息表中的选课人数数据
        boolean isUpdate = courseService.updateBatchById(courseList);
        if(!isUpdate) throw new CustomException(ResCodeEnum.SERVER_ERROR,"退选更新选课人数错误");

        return ResData.success(true, "删除成功");
    }
    //分页查课程信息
    public ResData<PageResVO<CoSelectionVO>> findPageCoSelection(FindPageCoSelectionDTO findPageCoSelectionDTO) {
        PageParamsDTO pageParamsDTO = findPageCoSelectionDTO.getPageParams();
        FindAllCoSelectionDTO otherParams = findPageCoSelectionDTO.getOtherParams();

        //验证pageNum，pageSize
        Integer pageNum = pageParamsDTO.getPageNum();
        Integer pageSize = pageParamsDTO.getPageSize();
        coSelectionPageUtils.validatePageNumPageSize(pageNum, pageSize);

        //FindAllStuDTO
        QueryWrapper<Student> stuWrapper = WrapperUtils.buildAllStuWrapper(otherParams.getStudent(), new QueryWrapper<>());
        List<Student> stuList = studentService.list(stuWrapper);
        if(stuList.size() == 0) return returnEmptyRes();  //直接返回空

        //FindAllTchDTO  在查询教师表中符合条件的list
        QueryWrapper<Teacher> tchWrapper = WrapperUtils.buildAllTchWrapper(otherParams.getTeacher(), new QueryWrapper<>());
        List<Teacher> tchList = teacherService.list(tchWrapper);
        if(tchList.size() == 0) return returnEmptyRes();  //直接返回空

        //拿到符合条件的学生学号list，待后续组装好课程唯一标识id的list后查询选课表
        ArrayList<String> stuIdList = new ArrayList<>();
        for(Student entity: stuList) stuIdList.add(entity.getStudentId());

        //拿到符合条件的教师工号list 以及 课程的额外条件 在课程表中查找符合条件课程唯一标识id的list
        ArrayList<String> workIdList = new ArrayList<>();
        for(Teacher entity: tchList) workIdList.add(entity.getWorkId());
        QueryWrapper<Course> wrapper = new QueryWrapper<>();
        wrapper.in(workIdList.size() != 0,"work_id",workIdList);
        QueryWrapper<Course> courseWrapper = WrapperUtils.buildAllCourseWrapper(otherParams.getCourse(), wrapper);
        List<Course> courseList = courseService.list(courseWrapper);
        if(courseList.size() == 0) return returnEmptyRes();  //直接返回空

        ArrayList<String> courseIdList = new ArrayList<>();
        for(Course entity: courseList) courseIdList.add(entity.getId());

        //用学号list 和 课程唯一标识id的list 去查询选课表
        String createTime = otherParams.getCreateTime();
        QueryWrapper<CoSelection> coSeWrapper = new QueryWrapper<>();
        coSeWrapper.in("student_id",stuIdList);
        coSeWrapper.in("course_id",courseIdList);
        coSeWrapper.like(StrUtil.isNotBlank(createTime),"create_time",createTime.split(" ")[0]);

        QueryWrapper<CoSelection> newQueryWrapper = coSelectionPageUtils.buildPageQueryWrapper(pageParamsDTO, coSeWrapper);

        Page<CoSelection> basePage = new Page<>(pageNum, pageSize);
        Page<CoSelection> resPage = page(basePage, newQueryWrapper);

        //组装CoSelectionVO
        ArrayList<CoSelectionVO> VOList = new ArrayList<>();
        for (CoSelection entity : resPage.getRecords()) {
            CoSelectionVO VO = new CoSelectionVO();
            BeanUtil.copyProperties(entity, VO);

            //组装CoSelectionVO的student属性
            for(Student stuEntity: stuList) {
                if(entity.getStudentId().equals(stuEntity.getStudentId())) {
                    StudentVO studentVO = new StudentVO();
                    BeanUtil.copyProperties(stuEntity, studentVO,"identityId");
                    VO.setStudent(studentVO);
                }
            }

            //组装CoSelectionVO的course属性
            for(Course courseEntity: courseList) {
                if(entity.getCourseId().equals(courseEntity.getId())) {
                    CourseVO courseVO = new CourseVO();
                    BeanUtil.copyProperties(courseEntity, courseVO);
                    try {
                        VO.setCourse(courseService.setCourseVO(courseEntity,courseVO));
                    } catch (Exception e) {
                        e.printStackTrace();
                        throw new CustomException(ResCodeEnum.SERVER_ERROR,"分页查选课记录填充课程信息失败");
                    }

                    //组装CoSelectionVO的teacher属性
                    for(Teacher tchEntity: tchList) {
                        if(courseEntity.getWorkId().equals(tchEntity.getWorkId())) {
                            TeacherVO teacherVO = new TeacherVO();
                            BeanUtil.copyProperties(tchEntity, teacherVO,"identityId");
                            VO.setTeacher(teacherVO);
                            VO.getCourse().setTchName(tchEntity.getName());
                        }
                    }
                }
            }
            VOList.add(VO);
        }

        //构造分页结果
        PageResVO<CoSelectionVO> pageResVO = coSelectionPageUtils.getConditionResVO(resPage);
        pageResVO.setRows(VOList);

        return ResData.success(pageResVO);
    }
    //导出教师某一课程的选课学生Excel表
    public void coSelectionExport(HttpServletResponse response, FileCoSelectionDTO fileCoSelectionDTO) {
        String courseId = fileCoSelectionDTO.getCourseId();
        if (StrUtil.isBlank(courseId)) throw new CustomException(ResCodeEnum.BAD_REQUEST, "课程id不能为空");

        QueryWrapper<Course> courseWrapper = new QueryWrapper<>();
        courseWrapper.eq("id",courseId);
        courseWrapper.select("teach_class","course_name","course_code");
        List<Course> courseList = courseService.list(courseWrapper);
        if(courseList.size() == 0) throw new CustomException(ResCodeEnum.BAD_REQUEST,"该课程id的信息不存在");
        if(courseList.size() > 1) throw new CustomException(ResCodeEnum.BAD_REQUEST,"错误地存在多个该课程id的信息");
        Course course = courseList.get(0);

        Integer sex = fileCoSelectionDTO.getSex();
        String college = fileCoSelectionDTO.getCollege();
        Integer grade = fileCoSelectionDTO.getGrade();
        String major = fileCoSelectionDTO.getMajor();
        String stuClass = fileCoSelectionDTO.getStuClass();
        QueryWrapper<Student> stuWrapper = new QueryWrapper<>();
        stuWrapper.eq(sex != null,"sex",sex);
        stuWrapper.eq(grade != null,"grade",grade);
        stuWrapper.like(StrUtil.isNotBlank(college),"college",college);
        stuWrapper.like(StrUtil.isNotBlank(major),"major",major);
        stuWrapper.eq(StrUtil.isNotBlank(stuClass),"stu_class",stuClass);
        List<Student> stuList = studentService.list(stuWrapper);
        LinkedList<String> studentIdList = new LinkedList<>();
        for (Student stu: stuList) studentIdList.addFirst(stu.getStudentId());

        QueryWrapper<CoSelection> wrapper = new QueryWrapper<>();
        wrapper.eq("course_id",courseId);
        wrapper.in(studentIdList.size() != 0,"student_id",studentIdList);
        List<CoSelection> entityList = list(wrapper);

        //1. 所有学生列表，每一项处理后拷贝到 ExportVO的列表中
        ArrayList<FileCoSelectionVO> exportVOList = new ArrayList<>();
        for (int i = 0; i < entityList.size(); i++) {
            CoSelection entity = entityList.get(i);
            FileCoSelectionVO exportVO = new FileCoSelectionVO();
            exportVO.setOrder(i + 1);
            BeanUtils.copyProperties(course, exportVO);
            for (Student stu: stuList) {
                if(entity.getStudentId().equals(stu.getStudentId())) {
                    exportVO.setGrade(stu.getGrade() + "级");
                    if (stu.getSex() == 1) exportVO.setSex("男");
                    else exportVO.setSex("女");
                    BeanUtils.copyProperties(stu, exportVO, "grade","sex");
                    break;
                }
            }
            exportVOList.add(exportVO);
        }

        //2. 在UserExportVO类中给每个属性用 @Alias(value = "中文名")的注解添加中文名

        try {
            //3. 用ExcelUtil获取 excelWriter对象
            ExcelWriter excelWriter = ExcelUtil.getWriter(true);

            //4.将UserExportVO的列表写入excelWriter中，并添加中文名表头
            excelWriter.write(exportVOList, true);

            //5.设置浏览器响应的格式
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
            String fileName = URLEncoder.encode(course.getCourseName() + "的选课学生信息", "UTF-8");  //设置文件名称,并进行编码
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");  //将文件名设置到 response的 header中

            //6.用response来货如输出流
            ServletOutputStream outputStream = response.getOutputStream();

            //7. 将输出流刷新到 excelWriter对象中，并关闭输出流和excelWriter对象
            excelWriter.flush(outputStream, true);
            outputStream.close();
            excelWriter.close();
        } catch (Exception e) {
            e.printStackTrace();
            throw new CustomException(ResCodeEnum.SERVER_ERROR,"导出课程选课学生信息表格失败");
        }
    }


    //工具方法 -------------------------------------------------------------------
    private ResData<PageResVO<CoSelectionVO>> returnEmptyRes() {
        //如果任何一个为空，说明没有该学生，或者没有该课程。进而选课表中就不会有数据，就直接返回空。
        PageResVO<CoSelectionVO> emptyPageResVO = new PageResVO<>();
        emptyPageResVO.setRows(new ArrayList<>());
        return ResData.success(emptyPageResVO);
    }

    private Course getSelectedNumEntity(String courseId) {
        QueryWrapper<CoSelection> coSelectedWrapper = new QueryWrapper<>();
        coSelectedWrapper.eq("course_id",courseId);
        long coSelectedCount = count(coSelectedWrapper);

        Course course = new Course();
        course.setId(courseId);
        course.setSelectedNum(coSelectedCount);
        return course;
    }
}
